﻿#include "FileSystem.h"
#include "XyNet/Common/Exception.h"
#include "XyNet/Common/StringConverter.h"

namespace XyNet
{
	namespace FileSystem
	{
		Path::Path() {}

#ifdef XYNET_PLATFORM_WINDOWS
		Path::Path(const char* diskPath)
			: _boostPath(StringConverter::utf8ToWideString(diskPath)) {}

		Path::Path(const string& diskPath)
			: _boostPath(StringConverter::utf8ToWideString(diskPath)) {}
#else
		Path::Path(const char* diskPath) : _boostPath(diskPath) {}

		Path::Path(const string& diskPath) : _boostPath(diskPath) {}
#endif

		Path::Path(const boost::filesystem::path& diskPath) : _boostPath(diskPath) {}

		Path& Path::operator+=(const Path& path)
		{
#ifdef XYNET_PLATFORM_WINDOWS
			_boostPath = _boostPath.wstring() + path._boostPath.wstring();
#else
			_boostPath = _boostPath.string() + path._boostPath.string();
#endif
			return *this;
		}

		Path& Path::operator/=(const Path& path)
		{
			_boostPath /= path._boostPath;
			return *this;
		}

		bool Path::operator==(const Path& path) const
		{
			return _boostPath == path._boostPath;
		}

		bool Path::operator<(const Path& path) const
		{
			return _boostPath < path._boostPath;
		}

		string Path::toUtf8() const
		{
#ifdef XYNET_PLATFORM_WINDOWS
			return StringConverter::wideStringToUtf8(_boostPath.wstring());
#else
			return _boostPath.string();
#endif
		}

		string Path::toLocal() const
		{
			return _boostPath.string();
		}

		bool Path::isEmpty() const
		{
			return _boostPath.empty();
		}

		void Path::clear()
		{
			_boostPath.clear();
		}

		const boost::filesystem::path& Path::getBoostPath() const
		{
			return _boostPath;
		}

		Path& Path::removeFilename()
		{
			_boostPath.remove_filename();
			return *this;
		}

		Path& Path::replaceExtension(const Path& newExtension /*= Path()*/)
		{
			_boostPath.replace_extension(newExtension._boostPath);
			return *this;
		}

		Path Path::getRootPath() const
		{
			return _boostPath.root_path();
		}

		Path Path::getRootName() const
		{
			return _boostPath.root_name();
		}

		Path Path::getRootDirectory() const
		{
			return _boostPath.root_directory();
		}

		Path Path::getRelativePath() const
		{
			return _boostPath.relative_path();
		}

		Path Path::getParentPath(int level) const
		{
			assert(level >= 1);
			boost::filesystem::path parentPath = _boostPath;
			for (int i = 0; i < level; ++i)
			{
				assert(!parentPath.empty() && "空路径");
				parentPath = parentPath.parent_path();
			}
			return parentPath;
		}

		Path Path::getChildPath(int level) const
		{
			assert(level >= 1);
			Path childPath;
			auto iter = getIterator();
			while (iter.moveNext())
			{
				if (level > 0)
				{
					--level;
					iter.getCurrent();
				}
				else
				{
					childPath /= iter.getCurrent();
				}
			}
			assert(!childPath.isEmpty() && "空路径");
			return childPath;
		}

		Path Path::getFilename() const
		{
			return _boostPath.filename();
		}

		Path Path::getStem() const
		{
			return _boostPath.stem();
		}

		Path Path::getExtension() const
		{
			return _boostPath.extension();
		}

		bool Path::hasRootPath() const
		{
			return _boostPath.has_root_path();
		}

		bool Path::hasRootName() const
		{
			return _boostPath.has_root_name();
		}

		bool Path::hasRootDirectory() const
		{
			return _boostPath.has_root_directory();
		}

		bool Path::hasRelativePath() const
		{
			return _boostPath.has_relative_path();
		}

		bool Path::hasParentPath() const
		{
			return _boostPath.has_parent_path();
		}

		bool Path::hasFilename() const
		{
			return _boostPath.has_filename();
		}

		bool Path::hasStem() const
		{
			return _boostPath.has_stem();
		}

		bool Path::hasExtension() const
		{
			return _boostPath.has_extension();
		}

		bool Path::isAbsolute() const
		{
			return _boostPath.is_absolute();
		}

		bool Path::isRelative() const
		{
			return _boostPath.is_relative();
		}

		ConstIterator<Path> Path::getIterator() const
		{
			return ConstIterator<Path>(std::make_shared<PathIteratorImpl>(this));
		}

		ConstIterator<Path> Path::getDirectoryIterator() const
		{
			if (!isDirectory(*this))
				throw XYNET_EXCEPTION("指定路径不是目录：" + toUtf8());
			return ConstIterator<Path>(std::make_shared<DirectoryIteratorImpl>(this));
		}

		ConstIterator<Path> Path::getRecursiveDirectoryIterator(int maxLevel /*= -1*/) const
		{
			if (!isDirectory(*this))
				throw XYNET_EXCEPTION("指定路径不是目录：" + toUtf8());
			return ConstIterator<Path>(std::make_shared<RecursiveDirectoryIterator>(this));
		}

		XYNET_API bool isPortable(const Path& path)
		{
			return boost::filesystem::portable_posix_name(path.toUtf8());
		}

		XYNET_API bool isExists(const Path& path)
		{
			return boost::filesystem::exists(path.getBoostPath());
		}

		XYNET_API bool isFile(const Path& path)
		{
			return isExists(path) && !boost::filesystem::is_directory(path.getBoostPath());
		}

		XYNET_API bool isDirectory(const Path& path)
		{
			return isExists(path) && boost::filesystem::is_directory(path.getBoostPath());
		}

		XYNET_API int64 getFileSize(const Path& path)
		{
			//assert(isFile(path));
			return numeric_cast<int64>(boost::filesystem::file_size(path.getBoostPath()));
		}

		XYNET_API time_t getLastWriteTime(const Path& path)
		{
			return boost::filesystem::last_write_time(path.getBoostPath());
		}

		XYNET_API Path getInitialPath()
		{
			return boost::filesystem::initial_path();
		}

		XYNET_API Path getCurrentPath()
		{
			return boost::filesystem::current_path();
		}

		XYNET_API void setCurrentPath(const Path& path)
		{
			if (isFile(path))
				throw XYNET_EXCEPTION("路径指向的不是目录：" + path.toUtf8());
			boost::filesystem::current_path(path.getBoostPath());
		}

		XYNET_API Path absolute(const Path& path)
		{
			return boost::filesystem::absolute(path.getBoostPath());
		}

		XYNET_API Path canonical(const Path& path)
		{
			return boost::filesystem::canonical(path.getBoostPath());
		}

		XYNET_API void createDirectories(const Path& path)
		{
			if (isFile(path))
				throw XYNET_EXCEPTION("路径指向的不是目录：" + path.toUtf8());
			boost::filesystem::create_directories(path.getBoostPath());
		}

		XYNET_API void rename(const Path& oldPath, const Path& newPath)
		{
			assert(isExists(oldPath));
			createDirectories(newPath.getParentPath());
			boost::filesystem::rename(oldPath.getBoostPath(), newPath.getBoostPath());
		}

		XYNET_API void copy(const Path& fromPath, const Path& toPath)
		{
			assert(isExists(fromPath));
			boost::filesystem::copy(fromPath.getBoostPath(), toPath.getBoostPath());
		}

		XYNET_API void remove(const Path& path)
		{
			boost::filesystem::remove_all(path.getBoostPath());
		}

		XYNET_API bool operator<(const Path& path1, const Path& path2)
		{
			return path1.getBoostPath() < path2.getBoostPath();
		}

		//////////////////////////////////////////////////////////////////////////

		Path::PathIteratorImpl::PathIteratorImpl(const Path* path)
			: RangeIteratorImpl<boost::filesystem::path, Path>(&path->getBoostPath()) {}

		Path::PathIteratorImpl::~PathIteratorImpl() {}

		shared_ptr<IteratorImpl<Path>> Path::PathIteratorImpl::clone() const
		{
			return make_shared<PathIteratorImpl>(*this);
		}

		Path Path::PathIteratorImpl::getCurrent() const
		{
			assert(_current != _range->end());
			return *_current;
		}

		//////////////////////////////////////////////////////////////////////////

		Path::DirectoryIteratorImpl::DirectoryIteratorImpl(const Path* path)
			: IteratorImpl<Path>(), _path(path), _hasNext(true) {}

		Path::DirectoryIteratorImpl::~DirectoryIteratorImpl() {}

		shared_ptr<IteratorImpl<Path>> Path::DirectoryIteratorImpl::clone() const
		{
			return make_shared<DirectoryIteratorImpl>(*this);
		}

		bool Path::DirectoryIteratorImpl::moveNext() const
		{
			if (_hasNext)
			{
				if (_current == IteratorType())
					_current = IteratorType(_path->getBoostPath());
				else
					++_current;

				if (_current == IteratorType())
					_hasNext = false;
			}
			return _hasNext;
		}

		Path Path::DirectoryIteratorImpl::getCurrent() const
		{
			assert(_current != IteratorType());
			return _current->path();
		}

		//////////////////////////////////////////////////////////////////////////

		Path::RecursiveDirectoryIterator::RecursiveDirectoryIterator(
			const Path* path, int maxLevel /*= -1*/)
			: IteratorImpl<Path>(), _path(path), _maxLevel(maxLevel), _hasNext(true) {}

		Path::RecursiveDirectoryIterator::~RecursiveDirectoryIterator() {}

		shared_ptr<IteratorImpl<Path>> Path::RecursiveDirectoryIterator::clone() const
		{
			return make_shared<RecursiveDirectoryIterator>(*this);
		}

		bool Path::RecursiveDirectoryIterator::moveNext() const
		{
			if (_hasNext)
			{
				if (_current == IteratorType())
					_current = IteratorType(_path->getBoostPath());
				else
					++_current;

				if (_current == IteratorType())
					_hasNext = false;
				else
				{
					if (_current.level() == _maxLevel)
						_current.no_push();
				}
			}
			return _hasNext;
		}

		Path Path::RecursiveDirectoryIterator::getCurrent() const
		{
			assert(_current != IteratorType());
			return _current->path();
		}

		//////////////////////////////////////////////////////////////////////////

		Bytes File::readAllBytes(const Path& path)
		{
			if (!isFile(path))
				throw XYNET_EXCEPTION("路径指向的不是文件：" + path.toUtf8());

			boost::filesystem::ifstream fileStream(
				path.getBoostPath(), std::ios_base::in | std::ios_base::binary);
			assert(fileStream && ("无法载入文件：" + path.toUtf8()).c_str());

			// 这个方式读取太慢，慢100倍
			//Bytes bytes;
			//bytes.assign(std::istreambuf_iterator<char>(fileStream),
			//	std::istreambuf_iterator<char>());

			auto size = FileSystem::getFileSize(path);
			Bytes bytes(numeric_cast<size_t>(size), 0);
			fileStream.read((char*)bytes.data(), bytes.size());

			fileStream.close();
			return bytes;
		}

		void File::writeAllBytes(const Path& path, const Bytes& bytes)
		{
			//assert(Path::isExists(path));	// 不存在时可以创建
			if (isDirectory(path))
				throw XYNET_EXCEPTION("路径指向的是目录：" + path.toUtf8());
			if (!path.getParentPath().isEmpty())
				createDirectories(path.getParentPath());

			boost::filesystem::ofstream fileStream(
				path.getBoostPath(), std::ios_base::out | std::ios_base::binary);
			assert(fileStream && ("无法保存文件：" + path.toUtf8()).c_str());

			//fileStream << bytes.data();
			fileStream.write(bytes.data(), bytes.size());

			fileStream.close();
		}

		void File::appendText(const Path& path, const string& text)
		{
			if (isDirectory(path))
				throw XYNET_EXCEPTION("路径指向的是目录：" + path.toUtf8());
			if (!path.getParentPath().isEmpty())
				createDirectories(path.getParentPath());

			boost::filesystem::ofstream fileStream(
				path.getBoostPath(), std::ios_base::out | std::ios_base::app);
			assert(fileStream && ("无法保存文件：" + path.toUtf8()).c_str());

			fileStream << text;

			fileStream.close();
		}

	}
}